package br.org.furb.compilador.model.analisador;

import java.util.Stack;

public class Sintatico implements Constants {
	@SuppressWarnings("rawtypes")
	private Stack stack = new Stack();
	private Token currentToken;
	private Token previousToken;
	private Lexico scanner;
	private Semantico semanticAnalyser;

	private static final boolean isTerminal(int x) {
		return x < FIRST_NON_TERMINAL;
	}

	private static final boolean isNonTerminal(int x) {
		return x >= FIRST_NON_TERMINAL && x < FIRST_SEMANTIC_ACTION;
	}

	@SuppressWarnings("unused")
	private static final boolean isSemanticAction(int x) {
		return x >= FIRST_SEMANTIC_ACTION;
	}

	private boolean step() throws LexicalError, SyntaticError, SemanticError {
		if (currentToken == null) {
			int line = 0;
			int pos = 0;
			if (previousToken != null) {
				pos = previousToken.getPosition()
						+ previousToken.getLexeme().length();
				line = previousToken.getLine();
			}

			currentToken = new Token(DOLLAR, "$", pos, line);
		}

		int x = ((Integer) stack.pop()).intValue();
		int a = currentToken.getId();

		if (x == EPSILON) {
			return false;
		} else if (isTerminal(x)) {
			if (x == a) {
				if (stack.empty())
					return true;
				else {
					previousToken = currentToken;
					currentToken = scanner.nextToken();
					return false;
				}
			} else {
				// throw new SyntaticError(PARSER_ERROR[x],
				// currentToken.getPosition());
				throw new SyntaticError(getError(x));
			}
		} else if (isNonTerminal(x)) {
			if (pushProduction(x, a))
				return false;
			else
				// throw new SyntaticError(PARSER_ERROR[x],
				// currentToken.getPosition());
				throw new SyntaticError(getError(x));
		} else // isSemanticAction(x)
		{
			semanticAnalyser.executeAction(x - FIRST_SEMANTIC_ACTION,
					previousToken);
			return false;
		}
	}

	@SuppressWarnings("unchecked")
	private boolean pushProduction(int topStack, int tokenInput) {
		int p = PARSER_TABLE[topStack - FIRST_NON_TERMINAL][tokenInput - 1];
		if (p >= 0) {
			int[] production = PRODUCTIONS[p];
			// empilha a produção em ordem reversa
			for (int i = production.length - 1; i >= 0; i--) {
				stack.push(new Integer(production[i]));
			}
			return true;
		} else
			return false;
	}

	@SuppressWarnings("unchecked")
	public void parse(Lexico scanner, Semantico semanticAnalyser)
			throws LexicalError, SyntaticError, SemanticError {
		this.scanner = scanner;
		this.semanticAnalyser = semanticAnalyser;

		stack.clear();
		stack.push(new Integer(DOLLAR));
		stack.push(new Integer(START_SYMBOL));

		currentToken = scanner.nextToken();

		while (!step())
			;
	}

	private String getError(int index) {
		String msg = "Erro na linha "
				+ Integer.toString(currentToken.getLine()) +
				// " posição: " + currentToken.getPosition() +
				" - encontrado ";

		/*
		 * if (errorType == ErrorSintatico.ERROR_TERMINAL){
		 * 
		 * }else{
		 * 
		 * }
		 */
		if (currentToken.getClasse().equals("identificador")) {
			msg = msg + currentToken.getClasse() + " ("
					+ currentToken.getLexeme() + ") ";
		} else if (currentToken.getLexeme().equals("$")) {
			msg = msg + "EOF ";
		} else {
			msg = msg + currentToken.getLexeme() + " ";
		}

		return msg + PARSER_ERROR[index];
	}

}
